use crate::context::{generate_rsa_key_pair, get_file_or_create, read_to_string};
use crate::{get_timestamp, AppResult};
use openssl::asn1::{Asn1Integer, Asn1Time};
use openssl::bn::BigNum;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private, Public};
use openssl::x509::extension::{AuthorityKeyIdentifier, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier};
use openssl::x509::{X509NameBuilder, X509};
use std::time::SystemTime;
use const_format::concatcp;
use tokio::fs;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use tokio::sync::OnceCell;
use tracing::info;
use crate::license_server::SERVER_UID;
/** 授权码根密钥文件路径 */
pub const CODE_ROOT_KEY_FILE_NAME: &str = "external/certificate/code-root.pem";

/** 服务器根密钥文件路径 */
pub const SERVER_ROOT_KEY_FILE_NAME: &str = "external/certificate/server-root.pem";

/** 4096位私钥文件路径 */
pub const PRIVATE_KEY_FILE_NAME: &str = "external/certificate/private.pem";

/** 4096位公钥文件路径 */
pub const PUBLIC_KEY_FILE_NAME: &str = "external/certificate/public.pem";

/** 授权码证书文件路径 */
const CODE_CET_FILE_NAME: &str = "external/certificate/code-ca.pem";

/** 服务器证书文件路径 */
const SERVER_CET_FILE_NAME: &str = "external/certificate/server-ca.pem";

/** 服务器子证书文件路径 */
const SERVER_CHILD_CET_FILE_NAME: &str = "external/certificate/server-child-ca.pem";

/** 授权码签名CA的DN */
const CODE_CA_DN: &str = "JetProfile CA";

/** 许可证服务器CA的DN */
const LICENSE_SERVER_CA_DN: &str = "License Servers CA";

/** 应用程序主体DN */
const APP_SUBJECT_DN: &str = "Jetbrains-Helper";

/** 许可证服务器域名 */
const LICENSE_SERVER_DOMAIN: &str = concatcp!(SERVER_UID,".lsrv.jetbrains.com");

/** 许可证服务器主体DN */
static CERTIFICATE_CONTEXT: OnceCell<CertificateContext> = OnceCell::const_new();
pub async fn get_certificate_context() -> &'static CertificateContext {
    CERTIFICATE_CONTEXT
        .get_or_init(|| async {
            let result = async {
                let mut code_root_key_file = get_file_or_create(CODE_ROOT_KEY_FILE_NAME).await?;
                let mut server_root_key_file = get_file_or_create(SERVER_ROOT_KEY_FILE_NAME).await?;
                let server_root_x509 = X509::from_pem(read_to_string(&mut server_root_key_file).await?.as_ref())?;
                let code_root_x509 = X509::from_pem(read_to_string(&mut code_root_key_file).await?.as_ref())?;
                if is_certificate_generation_required().await? {
                    info!("检测到证书文件缺失，开始生成证书...");
                    let (private_key, public_key) = generate_rsa_key_pair(4096)?;
                    let mut private_key_file = get_file_or_create(PRIVATE_KEY_FILE_NAME).await?;
                    let mut public_key_file = get_file_or_create(PUBLIC_KEY_FILE_NAME).await?;
                    private_key_file
                        .write_all(private_key.private_key_to_pem_pkcs8()?.as_ref())
                        .await?;
                    private_key_file.flush().await?;
                    public_key_file
                        .write_all(public_key.public_key_to_pem()?.as_ref())
                        .await?;
                    public_key_file.flush().await?;
                    let code_certificate =
                        generate_ca(&private_key, &public_key, CODE_CA_DN).await?;
                    let server_certificate =
                        generate_ca(&private_key, &public_key, LICENSE_SERVER_CA_DN).await?;
                    let server_child_certificate = generate_server_child_ca(
                        &private_key,
                        &public_key,
                        &server_root_x509,
                    )
                    .await?;
                    let mut code_ca_file = get_file_or_create(CODE_CET_FILE_NAME).await?;
                    code_ca_file
                        .write_all(code_certificate.to_pem()?.as_ref())
                        .await?;
                    let mut server_ca_file = get_file_or_create(SERVER_CET_FILE_NAME).await?;
                    server_ca_file
                        .write_all(server_certificate.to_pem()?.as_ref())
                        .await?;
                    let mut server_child_ca_file =
                        get_file_or_create(SERVER_CHILD_CET_FILE_NAME).await?;
                    server_child_ca_file
                        .write_all(server_child_certificate.to_pem()?.as_ref())
                        .await?;
                    info!("证书生成成功！");
                    let cache=CertificateCache {
                        code_root_crt: code_root_x509,
                        server_root_crt: server_root_x509,
                        private_key,
                        public_key,
                        code_cet:code_certificate,
                        server_cet:server_certificate,
                        server_child_cet:server_child_certificate
                    };
                    AppResult::Ok(CertificateContext {
                        code_cet_file: code_ca_file,
                        server_cet_file: server_ca_file,
                        server_child_cet_file: server_child_ca_file,
                        code_root_key_file,
                        server_root_key_file,
                        private_key_file,
                        public_key_file,
                        cache,
                    })
                } else {
                    info!("证书文件已存在，开始加载...");
                    let mut code_ca_file = get_file_or_create(CODE_CET_FILE_NAME).await?;
                    let mut server_ca_file = get_file_or_create(SERVER_CET_FILE_NAME).await?;
                    let mut server_child_ca_file = get_file_or_create(SERVER_CHILD_CET_FILE_NAME).await?;
                    let mut private_key_file = get_file_or_create(PRIVATE_KEY_FILE_NAME).await?;
                    let mut public_key_file = get_file_or_create(PUBLIC_KEY_FILE_NAME).await?;
                    let private_key = PKey::private_key_from_pem(read_to_string(&mut private_key_file).await?.as_ref())?;
                    let public_key = PKey::public_key_from_pem(read_to_string(&mut public_key_file).await?.as_ref())?;
                    let code_certificate = X509::from_pem(read_to_string(&mut code_ca_file).await?.as_ref())?;
                    let server_certificate = X509::from_pem(read_to_string(&mut server_ca_file).await?.as_ref())?;
                    let server_child_certificate = X509::from_pem(read_to_string(&mut server_child_ca_file).await?.as_ref())?;
                    let cache=CertificateCache {
                        code_root_crt: code_root_x509,
                        server_root_crt: server_root_x509,
                        private_key,
                        public_key,
                        code_cet:code_certificate,
                        server_cet:server_certificate,
                        server_child_cet:server_child_certificate
                    };
                    info!("证书加载成功！");
                    Ok(CertificateContext {
                        code_cet_file: code_ca_file,
                        server_cet_file: server_ca_file,
                        server_child_cet_file: server_child_ca_file,
                        code_root_key_file,
                        server_root_key_file,
                        private_key_file,
                        public_key_file,
                        cache
                    })
                }
            };
            result.await.unwrap()
        })
        .await
}
async fn generate_server_child_ca(
    pri_key: &PKey<Private>,
    public_key: &PKey<Public>,
    root_ca: &X509,
) -> AppResult<X509> {
    let mut x509builder = X509::builder()?;
    x509builder.set_version(2)?;
    let asn1integer = Asn1Integer::from_bn(
        BigNum::from_dec_str(
            SystemTime::now()
                .duration_since(SystemTime::UNIX_EPOCH)?
                .as_secs()
                .to_string()
                .as_ref(),
        )?
        .as_ref(),
    )?;
    x509builder.set_serial_number(&asn1integer)?;
    let mut x509name_builder = X509NameBuilder::new()?;
    x509name_builder.append_entry_by_nid(Nid::COMMONNAME, LICENSE_SERVER_DOMAIN)?;
    x509builder.set_subject_name(x509name_builder.build().as_ref())?;
    let mut issuer_name_builder = X509NameBuilder::new()?;
    issuer_name_builder.append_entry_by_nid(Nid::COMMONNAME, LICENSE_SERVER_CA_DN)?;
    x509builder.set_issuer_name(issuer_name_builder.build().as_ref())?;
    x509builder.set_pubkey(&public_key)?;
    x509builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?;
    x509builder.set_not_after(Asn1Time::days_from_now(365*50)?.as_ref())?;
    x509builder.sign(pri_key, MessageDigest::sha256())?;
    let ctx = x509builder.x509v3_context(Some(&root_ca),None);
    let result = ExtendedKeyUsage::new().server_auth().client_auth().build()?;
    let result1 = SubjectAlternativeName::new()
        .dns(LICENSE_SERVER_DOMAIN)
        .build(&ctx);
    let result2 = KeyUsage::new()
        .digital_signature()
        .key_encipherment()
        .key_agreement()
        .critical()
        .build();
    let result3 = AuthorityKeyIdentifier::new()
        .issuer(true)
        .keyid(true)
        .build(&ctx);
    let result4 = SubjectKeyIdentifier::new()
        .build(&ctx);
    x509builder.append_extension(result)?;
    x509builder.append_extension(result1?)?;
    x509builder.append_extension(result2?)?;
    x509builder.append_extension(result3?)?;
    x509builder.append_extension(result4?)?;
    Ok(x509builder.build())
}
async fn generate_ca(
    pri_key: &PKey<Private>,
    public_key: &PKey<Public>,
    issuer_name: &str,
) -> AppResult<X509> {
    let mut x509builder = X509::builder()?;
    x509builder.set_version(2)?;
    let asn1integer = BigNum::from_dec_str(get_timestamp().to_string().as_ref())?.to_asn1_integer()?;
    x509builder.set_serial_number(&asn1integer)?;
    let mut x509name_builder = X509NameBuilder::new()?;
    x509name_builder.append_entry_by_nid(Nid::COMMONNAME, APP_SUBJECT_DN)?;
    x509builder.set_subject_name(x509name_builder.build().as_ref())?;
    let mut issuer_name_builder = X509NameBuilder::new()?;
    issuer_name_builder.append_entry_by_nid(Nid::COMMONNAME, issuer_name)?;
    x509builder.set_issuer_name(issuer_name_builder.build().as_ref())?;
    x509builder.set_pubkey(&public_key)?;
    x509builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?;
    x509builder.set_not_after(Asn1Time::days_from_now(365 * 50)?.as_ref())?;
    x509builder.sign(pri_key, MessageDigest::sha256())?;
    Ok(x509builder.build())
}
async fn is_certificate_generation_required() -> AppResult<bool> {
    Ok(!fs::try_exists(PRIVATE_KEY_FILE_NAME).await?
        || !fs::try_exists(PUBLIC_KEY_FILE_NAME).await?
        || !fs::try_exists(CODE_CET_FILE_NAME).await?
        || !fs::try_exists(SERVER_CET_FILE_NAME).await?
        || !fs::try_exists(SERVER_CHILD_CET_FILE_NAME).await?)
}
#[derive(Debug)]
pub struct CertificateContext {
    code_root_key_file: File,
    server_root_key_file: File,
    private_key_file: File,
    public_key_file: File,
    code_cet_file: File,
    server_cet_file: File,
    server_child_cet_file: File,
    cache: CertificateCache,
}
#[derive(Debug)]
pub struct CertificateCache {
    code_root_crt: X509,
    server_root_crt: X509,
    private_key: PKey<Private>,
    public_key: PKey<Public>,
    code_cet: X509,
    server_cet: X509,
    server_child_cet: X509,
}
impl CertificateCache {
    #[inline]
    pub fn get_code_root_crt(&self) -> &X509 {
        &self.code_root_crt
    }
    #[inline]
    pub fn get_server_root_crt(&self) -> &X509 {
        &self.server_root_crt
    }
    #[inline]
    pub fn get_private_key(&self) -> &PKey<Private> {
        &self.private_key
    }
    #[inline]
    pub fn get_public_key(&self) -> &PKey<Public> {
        &self.public_key
    }
    #[inline]
    pub fn get_code_cet(&self) -> &X509 {
        &self.code_cet
    }
    #[inline]
    pub fn get_server_cet(&self) -> &X509 {
        &self.server_cet
    }
    #[inline]
    pub fn get_server_child_cet(&self) -> &X509 {
        &self.server_child_cet
    }
}
impl CertificateContext {
    #[inline]
    pub fn cache(&self)->&CertificateCache{
        &self.cache
    }
    #[inline]
    pub fn get_code_root_key_file(&self) -> &File {
        &self.code_root_key_file
    }
    #[inline]
    pub fn get_server_root_key_file(&self) -> &File {
        &self.server_root_key_file
    }
    #[inline]
    pub fn get_private_key_file(&self) -> &File {
        &self.private_key_file
    }
    #[inline]
    pub fn get_public_key_file(&self) -> &File {
        &self.public_key_file
    }
    #[inline]
    pub fn get_code_cet_file(&self) -> &File {
        &self.code_cet_file
    }
    #[inline]
    pub fn get_server_cet_file(&self) -> &File {
        &self.server_cet_file
    }
    #[inline]
    pub fn get_server_child_cet_file(&self) -> &File {
        &self.server_child_cet_file
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tracing_subscriber::fmt;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::util::SubscriberInitExt;

    #[tokio::test]
    async fn test_generate_certificate()->AppResult<()> {
        // 只有注册 subscriber 后， 才能在控制台上看到日志输出
        tracing_subscriber::registry()
            .with(
                fmt::layer()
                    .with_line_number(true)
                    .with_thread_names(true)
                    .compact(),
            )
            .init();
        let context = get_certificate_context().await;
        println!("{:?}", context);
        Ok(())
    }
}
